{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "83fcadf3",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-2/chatbot-summarization.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239436-lesson-5-chatbot-w-summarizing-messages-and-memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b651ead9-5504-45ee-938d-f91ac78dddd1",
   "metadata": {},
   "source": [
    "# Chatbot with message summarization\n",
    "\n",
    "## Review\n",
    "\n",
    "We've covered how to customize graph state schema and reducer. \n",
    " \n",
    "We've also shown a number of ways to trim or filter messages in graph state. \n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, let's take it one step further! \n",
    "\n",
    "Rather than just trimming or filtering messages, we'll show how to use LLMs to produce a running summary of the conversation.\n",
    " \n",
    "This allows us to retain a compressed representation of the full conversation, rather than just removing it with trimming or filtering.\n",
    "\n",
    "We'll incorporate this summarization into a simple Chatbot.  \n",
    "\n",
    "And we'll equip that Chatbot with memory, supporting long-running conversations without incurring high token cost / latency. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "000a6daa-92ad-4e57-a060-d1c81176eb0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langchain_core langgraph langchain_openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "09201a62",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfddfce9-3a9b-4b35-a76d-28265515aabd",
   "metadata": {},
   "source": [
    "We'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "464856d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "_set_env(\"LANGCHAIN_API_KEY\")\n",
    "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
    "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "537ade30-6a0e-4b6b-8bcd-ce90790b6392",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "model = ChatOpenAI(model=\"gpt-4o\",temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db3afac3-8b7a-45db-a3c1-7e4125c1bc8b",
   "metadata": {},
   "source": [
    "We'll use `MessagesState`, as before.\n",
    "\n",
    "In addition to the built-in `messages` key, we'll now include a custom key (`summary`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "948e60f0-5c76-4235-b40e-cf523205d40e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import MessagesState\n",
    "class State(MessagesState):\n",
    "    summary: str"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6855ea31-5cc1-4277-a189-0b72459f67ec",
   "metadata": {},
   "source": [
    "We'll define a node to call our LLM that incorporates a summary, if it exists, into the prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c3f7d19b-afe0-4381-9b1a-0a832b162e7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage\n",
    "\n",
    "# Define the logic to call the model\n",
    "def call_model(state: State):\n",
    "    \n",
    "    # Get summary if it exists\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "\n",
    "    # If there is summary, then we add it\n",
    "    if summary:\n",
    "        \n",
    "        # Add summary to system message\n",
    "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
    "\n",
    "        # Append summary to any newer messages\n",
    "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
    "    \n",
    "    else:\n",
    "        messages = state[\"messages\"]\n",
    "    \n",
    "    response = model.invoke(messages)\n",
    "    return {\"messages\": response}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6882042c-b42d-4d52-a6a7-6ec8efa72450",
   "metadata": {},
   "source": [
    "We'll define a node to produce a summary.\n",
    "\n",
    "Note, here we'll use `RemoveMessage` to filter our state after we've produced the summary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "78c7aa59-3760-4e76-93f1-bc713e3ec39e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def summarize_conversation(state: State):\n",
    "    \n",
    "    # First, we get any existing summary\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "\n",
    "    # Create our summarization prompt \n",
    "    if summary:\n",
    "        \n",
    "        # A summary already exists\n",
    "        summary_message = (\n",
    "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
    "            \"Extend the summary by taking into account the new messages above:\"\n",
    "        )\n",
    "        \n",
    "    else:\n",
    "        summary_message = \"Create a summary of the conversation above:\"\n",
    "\n",
    "    # Add prompt to our history\n",
    "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
    "    response = model.invoke(messages)\n",
    "    \n",
    "    # Delete all but the 2 most recent messages\n",
    "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
    "    return {\"summary\": response.content, \"messages\": delete_messages}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f982993e-f4be-4ff7-9a38-886f75398b3d",
   "metadata": {},
   "source": [
    "We'll add a conditional edge to determine whether to produce a summary based on the conversation length."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b507665d-7f5d-442a-b498-218c94c5dd8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import END\n",
    "# Determine whether to end or summarize the conversation\n",
    "def should_continue(state: State):\n",
    "    \n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    \n",
    "    messages = state[\"messages\"]\n",
    "    \n",
    "    # If there are more than six messages, then we summarize the conversation\n",
    "    if len(messages) > 6:\n",
    "        return \"summarize_conversation\"\n",
    "    \n",
    "    # Otherwise we can just end\n",
    "    return END"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a838f4c-7067-4f7f-a4c4-6654e11214cd",
   "metadata": {},
   "source": [
    "## Adding memory\n",
    "\n",
    "Recall that [state is transient](https://github.com/langchain-ai/langgraph/discussions/352#discussioncomment-9291220) to a single graph execution.\n",
    "\n",
    "This limits our ability to have multi-turn conversations with interruptions. \n",
    "\n",
    "As introduced at the end of Module 1, we can use [persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/) to address this! \n",
    " \n",
    "LangGraph can use a checkpointer to automatically save the graph state after each step.\n",
    "\n",
    "This built-in persistence layer gives us memory, allowing LangGraph to pick up from the last state update. \n",
    "\n",
    "As we previously showed, one of the easiest to work with is `MemorySaver`, an in-memory key-value store for Graph state.\n",
    "\n",
    "All we need to do is compile the graph with a checkpointer, and our graph has memory!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "1d57516d-f9f1-4d3c-a84a-7277b5ce6df6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import StateGraph, START\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(State)\n",
    "workflow.add_node(\"conversation\", call_model)\n",
    "workflow.add_node(summarize_conversation)\n",
    "\n",
    "# Set the entrypoint as conversation\n",
    "workflow.add_edge(START, \"conversation\")\n",
    "workflow.add_conditional_edges(\"conversation\", should_continue)\n",
    "workflow.add_edge(\"summarize_conversation\", END)\n",
    "\n",
    "# Compile\n",
    "memory = MemorySaver()\n",
    "graph = workflow.compile(checkpointer=memory)\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d0bd5d23-ac3b-4496-a049-9a9f97d2feb9",
   "metadata": {},
   "source": [
    "## Threads\n",
    "\n",
    "The checkpointer saves the state at each step as a checkpoint.\n",
    "\n",
    "These saved checkpoints can be grouped into a `thread` of conversation.\n",
    "\n",
    "Think about Slack as an analog: different channels carry different conversations.\n",
    "\n",
    "Threads are like Slack channels, capturing grouped collections of state (e.g., conversation).\n",
    "\n",
    "Below, we use `configurable` to set a thread ID.\n",
    "\n",
    "![state.jpg](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbadf3b379c2ee621adfd1_chatbot-summarization1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2566c93b-13e6-4a53-bc0f-b00fff691d30",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hi Lance! How can I assist you today?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You mentioned that your name is Lance. How can I help you today, Lance?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "That's awesome, Lance! The San Francisco 49ers have a rich history and a passionate fan base. Do you have a favorite player or a memorable game that stands out to you?\n"
     ]
    }
   ],
   "source": [
    "# Create a thread\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Start conversation\n",
    "input_message = HumanMessage(content=\"hi! I'm Lance\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()\n",
    "\n",
    "input_message = HumanMessage(content=\"i like the 49ers!\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "531e5b63-5e8b-486e-baa0-a45521e2fbc2",
   "metadata": {},
   "source": [
    "Now, we don't yet have a summary of the state because we still have < = 6 messages.\n",
    "\n",
    "This was set in `should_continue`. \n",
    "\n",
    "```\n",
    "    # If there are more than six messages, then we summarize the conversation\n",
    "    if len(messages) > 6:\n",
    "        return \"summarize_conversation\"\n",
    "```\n",
    "\n",
    "We can pick up the conversation because we have the thread."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "91b82aaa-17f9-49e2-9528-f4b22e23ebcb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values.get(\"summary\",\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "068a93e9-f716-4980-8edf-94115017d865",
   "metadata": {},
   "source": [
    "The `config` with thread ID allows us to proceed from the previously logged state!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "24b34f0f-62ef-4008-8e96-480cbe92ea3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Yes, Nick Bosa is indeed one of the highest-paid defensive players in the NFL. In September 2023, he signed a record-breaking contract extension with the San Francisco 49ers, making him the highest-paid defensive player at that time. His performance on the field has certainly earned him that recognition. It's great to hear you're a fan of such a talented player!\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/var/folders/l9/bpjxdmfx7lvd1fbdjn38y5dh0000gn/T/ipykernel_18661/23381741.py:23: LangChainBetaWarning: The class `RemoveMessage` is in beta. It is actively being worked on, so the API may change.\n",
      "  delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"i like Nick Bosa, isn't he the highest paid defensive player?\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "22f1b35f-e4bb-47f6-87b1-d84d8aed9aa9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Lance introduced himself and mentioned that he is a fan of the San Francisco 49ers. He specifically likes Nick Bosa and inquired if Bosa is the highest-paid defensive player. I confirmed that Nick Bosa signed a record-breaking contract extension in September 2023, making him the highest-paid defensive player at that time, and acknowledged Bosa's talent and Lance's enthusiasm for the player.\""
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values.get(\"summary\",\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad7cc0ab-905a-4037-b7cb-69db5b89591e",
   "metadata": {},
   "source": [
    "## LangSmith\n",
    "\n",
    "Let's review the trace!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
